package com.gitee.wsl.func.filter.bloomfilter.bean


/**
 * Contains functions to convert `int` indices into Bloom filter bit positions and visa versa.
 *
 *
 * The functions view an array of longs as a collection of bit maps each containing 64 bits. The bits are arranged
 * in memory as a little-endian long value. This matches the requirements of the BitMapExtractor interface.
 *
 * @since 4.5.0-M2
 */
object BitMaps {
    /** A bit shift to apply to an integer to divided by 64 (2^6).  */
    private const val DIVIDE_BY_64 = 6

    /**
     * Checks if the specified index bit is enabled in the array of bit maps.
     *
     *
     * If the bit specified by bitIndex is not in the bit map false is returned.
     *
     *
     * @param bitMaps  The array of bit maps.
     * @param bitIndex the index of the bit to locate.
     * @return `true` if the bit is enabled, `false` otherwise.
     * @throws IndexOutOfBoundsException if bitIndex specifies a bit not in the range being tracked.
     */
    fun contains(bitMaps: LongArray, bitIndex: Int): Boolean {
        return (bitMaps[getLongIndex(bitIndex)] and getLongBit(bitIndex)) != 0L
    }

    /**
     * Gets the filter bit mask for the specified bit index assuming the filter is using 64-bit
     * longs to store bits starting at index 0. The returned value is a `long` with only
     * 1 bit set.
     *
     *
     * The index is assumed to be positive. For a positive index the result will match
     * `1L << (bitIndex % 64)`.
     *
     *
     * *If the input is negative the behavior is not defined.*
     *
     * @param bitIndex the bit index (assumed to be positive)
     * @return the filter bit
     */
    fun getLongBit(bitIndex: Int): Long {
        // Bit shifts only use the first 6 bits. Thus it is not necessary to mask this
        // using 0x3f (63) or compute bitIndex % 64.
        // Note: If the index is negative the shift will be (64 - (bitIndex & 0x3f)) and
        // this will identify an incorrect bit.
        return 1L shl bitIndex
    }

    /**
     * Gets the filter index for the specified bit index assuming the filter is using 64-bit longs
     * to store bits starting at index 0.
     *
     *
     * The index is assumed to be positive. For a positive index the result will match
     * `bitIndex / 64`.
     *
     *
     * *The divide is performed using bit shifts. If the input is negative the behavior
     * is not defined.*
     *
     * @param bitIndex the bit index (assumed to be positive)
     * @return the index of the bit map in an array of bit maps.
     */
    fun getLongIndex(bitIndex: Int): Int {
        // An integer divide by 64 is equivalent to a shift of 6 bits if the integer is
        // positive.
        // We do not explicitly check for a negative here. Instead we use a
        // signed shift. Any negative index will produce a negative value
        // by sign-extension and if used as an index into an array it will throw an
        // exception.
        return bitIndex shr DIVIDE_BY_64
    }

    /**
     * Performs a modulus calculation on an unsigned long and a positive integer divisor.
     *
     *
     * This method computes the same result as [Long.remainderUnsigned]
     * but assumes that the divisor is an integer in the range 1 to 2<sup>31</sup> - 1 inclusive,
     * that is a strictly positive integer size.
     *
     *
     * *If the divisor is negative the behavior is not defined.*
     *
     * @param dividend an unsigned long value to calculate the modulus of.
     * @param divisor the divisor for the modulus calculation, must be strictly positive.
     * @return the remainder or modulus value.
     * @throws ArithmeticException if the divisor is zero
     * @see Long.remainderUnsigned
     */
    fun mod(dividend: Long, divisor: Int): Int {
        // See Hacker's Delight (2nd ed), section 9.3.
        // Assume divisor is positive.
        // Divide half the unsigned number and then double the quotient result.
        val quotient = (dividend ushr 1) / divisor shl 1
        val remainder = dividend - quotient * divisor
        // remainder in [0, 2 * divisor)
        return (if (remainder >= divisor) remainder - divisor else remainder).toInt()
    }

    /**
     * Creates a new bitmap for the number of bit maps (longs) required for the numberOfBits parameter.
     *
     *
     * *If the input is negative the behavior is not defined.*
     *
     * @param numberOfBits the number of bits to store in the array of bit maps.
     * @return a new bitmap.
     */
    fun newBitMap(numberOfBits: Int): LongArray {
        return LongArray(numberOfBitMaps(numberOfBits))
    }

    /**
     * Creates a new bitmap for given shape parameter.
     *
     * @param shape the shape.
     * @return a new bitmap.
     */
    fun newBitMap(shape: Shape): LongArray {
        return newBitMap(shape.numberOfBits)
    }

    /**
     * Calculates the number of bit maps (longs) required for the numberOfBits parameter.
     *
     *
     * *If the input is negative the behavior is not defined.*
     *
     * @param numberOfBits the number of bits to store in the array of bit maps.
     * @return the number of bit maps necessary.
     */
    fun numberOfBitMaps(numberOfBits: Int): Int {
        return (numberOfBits - 1 shr DIVIDE_BY_64) + 1
    }

    /**
     * Calculates the number of bit maps (longs) required for the shape parameter.
     *
     * @param shape the shape.
     * @return the number of bit maps necessary.
     */
    fun numberOfBitMaps(shape: Shape): Int {
        return numberOfBitMaps(shape.numberOfBits)
    }

    /**
     * Sets the bit in the bit maps.
     *
     * *Does not perform range checking*
     *
     * @param bitMaps  The array of bit maps.
     * @param bitIndex the index of the bit to set.
     * @throws IndexOutOfBoundsException if bitIndex specifies a bit not in the range being tracked.
     */
    fun set(bitMaps: LongArray, bitIndex: Int) {
        bitMaps[getLongIndex(bitIndex)] = bitMaps[getLongIndex(bitIndex)] or getLongBit(bitIndex)
    }
}
